package mireille.learning.devoir1;

import java.util.Arrays;
import java.util.Collections;
import mireille.learning.devoir1.Exceptions.CompteException;
import mireille.learning.devoir1.Exceptions.OperationPourCrediterNonAutoriseException;
import mireille.learning.devoir1.Exceptions.DebitNonAutoriseException;
import mireille.learning.devoir1.Exceptions.ErreurTypeDeCompteException;

/**
 * <p>
 * Classe permettant de gérer un ensemble de comptes bancaires. Les comptes
 * pourront être stockés dans un tableau d'objets de type CompteBancaire
 * </p>
 *
 * @author Mireille
 *
 */
public class AgenceBancaire {

    private CompteBancaire[] agenceBancaire; //tableau de comptes bancaires
    private int capacite;
    private int nbComptes;

    /**
     * Constructeur sans paramètres de la classe AgenceBancaire.
     */
    public AgenceBancaire() {
        agenceBancaire = new CompteBancaire[10];
        capacite = 10;  //variable qui mémorise la capacità du tableau ; dans ce cas, la capacité initiale = 0
        nbComptes = 0;  //compteur du nombre de comptes : aucun compte au départ
    }

    /**
     * Méthode permettant d'ajouter un objet de type compte bancaire à
     * l'ensemble des comptes. Le mot clé final a été ajouté pour optimiser
     * l'appel de cette méthode en empêchant sa redéfinition par héritage.
     *
     * @param c compte bancaire à ajouter au tableau
     */
    public final void ajouterCompte(CompteBancaire c) {

        //si la capacité maximale du tableau est atteinte	
        if (nbComptes == capacite) {
            redimensionner();
        }
        //le nouveau compte est ajouté dans la première case vide
        agenceBancaire[nbComptes] = c;
        nbComptes++; //le compteur du nombre de compte est incrémenté
    }

    /**
     * Méthode qui doublera la capacité du tableau si nécessaire.
     */
    private void redimensionner() {

        // doubler la capacité du tableau
        capacite = capacite * 2;

        //créer un nouveau tableau plus grand que l'ancien
        CompteBancaire[] nouveauTableau = new CompteBancaire[capacite];

        //recopier les comptes précédents dans le nouveau tableau
        for (int i = 0; i < nbComptes; i++) {
            nouveauTableau[i] = agenceBancaire[i];
        }
        // mise à jour du tableau de comptes 
        agenceBancaire = nouveauTableau;
    }

    /**
     * Méthode permettant de rechercher le compte d'un client dont le numéro est
     * passé en paramètre.
     *
     * @param numero numéro de compte à rechercher dans le tableau.
     * @return
     */
    public CompteBancaire rechercherCompte(int numero) {

        for (int i = 0; i < nbComptes; i++) {
            if ((agenceBancaire[i].getNumero()) == numero) {
                return agenceBancaire[i];
            }
        }
        return null;
    }

    /**
     * Méthode permettant de supprimer le compte d'un client de l'ensemble des
     * comptes. On suppose que l'ordre de stockage des comptes est indifférent.
     * Supprimer un compte se fera en écrasant le compte à supprimer par le
     * dernier compte du tableau
     *
     * @param c élément qui doit être supprimé du tableau
     */
    public void supprimerCompte(CompteBancaire c) {

        int identifiant = -1;
        for (int i = 0; i < nbComptes; i++) {
            if (agenceBancaire[i].equals(c)) {
                identifiant = i;// la recherche est mémorisée
                break; //fin de la recherche
            }
        }
        // si le compte c est trouvé dans le tableau des comptes bancaires
        if (identifiant != -1) {
            //écraser le compte à supprimer par le dernier compte
            //du tableau
            agenceBancaire[identifiant] = agenceBancaire[nbComptes - 1];
            nbComptes--; //mise à jour du nombre de comptes
        } // si le compte n'est pas trouvé
        else {
            System.out.println("Le compte bancaire numéro " + c.getNumero() + " au nom de " + c.getNom() + " n'a pas été trouvé.");
        }

    }

    /**
     * Méthode pour afficher les numéros de compte. Elle va permettre
     * d'effectuer des contrôles en phase de test. Afficher par exemple les
     * numéros de compte avant et après le tri.
     */
    public void afficherNumeroComptes() {
        for (int i = 0; i < nbComptes; i++) {
            System.out.println("Nom du client : " + agenceBancaire[i].getNom()
                    + " ; numéro du compte : " + agenceBancaire[i].getNumero());
        }
    }

    /**
     * Appliquer le traitement quotidien adapté sur chacun des comptes de
     * l'ensemble
     */
    public void traitementQuotidien() throws OperationPourCrediterNonAutoriseException, DebitNonAutoriseException {
        for (int i = 0; i < nbComptes; i++) {
            agenceBancaire[i].traitementQuotidien();
//            System.out.println("Le solde actuel du compte numéro " + agenceBancaire[i].getNumero() + " est de " + (double) ((int) agenceBancaire[i].getSolde() * 100 / 100) + " euros ");
        }
    }

    /**
     * Méthode qui décrit l'ensemble des comptes sous forme d'une chaîne de
     * caractères, un compte par ligne
     */
    public void ensembleComptes() {
        for (int i = 0; i < nbComptes; i++) {
            System.out.println(agenceBancaire[i].toString());
        }

    }

    /**
     * Méthode qui trie les éléments du tableau.
     */
    public void sort() {
        CompteBancaire[] copy = new CompteBancaire[nbComptes];
        for (int i = 0; i < nbComptes; i++) {
            copy[i] = agenceBancaire[i];
        }
        Arrays.sort(copy);
        for (int i = 0; i < nbComptes; i++) {
            agenceBancaire[i] = copy[i];
        }
    }

    /**
     * <p>Overload ou surcharge de la méthode ajouterCompte.</p>
     * <p>Cette méthode permet d'ajouter un compte au tableau constitué d'objets
     * de type compte bancaire. La signature de cette méthode est différente de
     * l'autre par le nombre de paramètres</p>
     *
     * @param type type de compte à ajouter au tableau des comptes bancaires
     * @param nom nom du compte
     * @param adresse adresse du compte
     * @throws ErreurTypeDeCompteException type d'erreur pouvant se produire
     * lorsque cette méthode est appelée
     */
    public void ajouterCompte(String type, String nom, String adresse) throws ErreurTypeDeCompteException {
        if ("E".equals(type)) {
            CompteEpargne ce = new CompteEpargne(nom, adresse);
            this.ajouterCompte(ce);
        } else if ("C".equals(type)) {
            CompteCourant cc = new CompteCourant(nom, adresse);
            this.ajouterCompte(cc);
        } else {
            throw new ErreurTypeDeCompteException();
        }


    }

    /**
     * Permet de débiter un montant sur un compte ayant en paramètre le numero
     * de compte et le montant.
     *
     * @param numero numéro du compte sur lequel débiter le montant
     * @param montant montant à débiter
     * @throws CompteException exception pouvant se produire
     */
    public void debiterSurLeCompte(int numero, double montant) throws DebitNonAutoriseException {
        //recherchons le numéro de compte d'un objet cb de type CompteBancaire dans l'agence bancaire 
        CompteBancaire cb = this.rechercherCompte(numero); 
        if (cb == null) { // le numero n'a pas été trouvé
            throw new DebitNonAutoriseException("Le compte n'a pas été trouvé !");
        }
        cb.debiter(montant);

    }

    /**
     * Méthode permettant de crediter un certain montant sur un compte en passant comme
     * paramètre le numéro du compte et le montant à crediter
     * @param numero numéro du compte
     * @param montant montant à créditer
     * @throws OperationPourCrediterNonAutoriseException exception pouvant se produire
     */
    public void crediterSurLeCompte(int numero, double montant) throws OperationPourCrediterNonAutoriseException {
        //recherchons le numéro de compte d'un objet cb de type CompteBancaire dans l'agence bancaire 
        CompteBancaire cb = this.rechercherCompte(numero);
        if (cb == null) { // si le numéro de compte n'a pas été trouvé
            throw new OperationPourCrediterNonAutoriseException("Le compte n'a pas été trouvé !");

        }
        cb.crediter(montant);
    }
}
